/**
 * \file pecoff.cc
 * \brief PE-COFF file format support
 * \author Corey Tabaka
 */

/*
   Copyright 2006 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <ktypes.h>
#include <pecoff.h>
#include <malloc.h>
#include <KernelSymbols.h>
#include <Module.h>
#include <Log.h>
#include <platform/Memory.h>
#include <platform/PageTable.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <i386/mem.h>

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#if 0
#define PE_DEBUG(fmt, arg...) Log::printf(LOG_LOW, fmt,##arg)
#else
#define PE_DEBUG(fmt, ...) ;
#endif

#if 0
#define PE_TRACE printf
#else
#define PE_TRACE(fmt, ...) ;
#endif

#if 1
#define PE_ERROR printf
#else
#define PE_ERROR(fmt, ...) ;
#endif

char *getSectName(const uint8 *f, image_section_header_t *sect) {
	if (sect->Name[0] == '/') {
		int index = atoi(sect->Name + 1);

		if (index > 0) {
			image_file_header_t *file = (image_file_header_t *) f;
			char *strTab = (char *) f + file->PointerToSymbolTable +
					file->NumberOfSymbols * sizeof(image_symbol_t);

			return strTab + index;
		} else {
			return "";
		}
	} else {
		return sect->Name;
	}
}

int getSymName(const uint8 *f, uint i, char *str) {
	image_file_header_t *file = (image_file_header_t *) f;

	if (i >= file->NumberOfSymbols) {
		PE_ERROR("index into symbol table (%u) is too large (%u max)\n", i,
			file->NumberOfSymbols);
		return -1;
	}

	image_symbol_t *sym = (image_symbol_t *)(f + file->PointerToSymbolTable)+i;
	if (sym->Short) {
		strncpy(str, (char *) sym->ShortName, 8);
		str[8] = '\0';
		return 0;
	} else if (sym->Long > 0) {
		char *strTab = (char *)f + file->PointerToSymbolTable +
			file->NumberOfSymbols * sizeof(image_symbol_t);

		strcpy(str, strTab + sym->Long);
		return 0;
	} else {
		strcpy(str, "");
		return 0;
	}
}

uint32 resolveSym(const uint8 *f, const uint8 *image,
	const char *name) {
	image_file_header_t *file = (image_file_header_t *) f;
	image_symbol_t *sym = 0;

	for (uint32 i=0; i < file->NumberOfSymbols; i++) {
		image_symbol_t *tmp = (image_symbol_t *)(f + file->PointerToSymbolTable)
			+ i;

		if (tmp->Short) {
			if (!strncmp(name, sym->ShortName, 8)) {
				sym = tmp;
				break;
			}
		} else if (tmp->Long > 0) {
			char *strTab = (char *)f + file->PointerToSymbolTable +
			file->NumberOfSymbols * sizeof(image_symbol_t);

			if (!strcmp(name, strTab + tmp->Long)) {
				sym = tmp;
				break;
			}
		}
	}

	if (sym && sym->SectionNumber > 0) {
		// internal symbol
		image_section_header_t * sect = (image_section_header_t *)(f
			+ sizeof(image_file_header_t) + file->SizeOfOptionalHeader)
			+ (sym->SectionNumber - 1);

		return (uint)image + sect->PhysicalAddress /*- sect->VirtualAddress*/
			+ sym->Value;
	} else {
		return 0;
	}
}

int getCoffSymbol(const uint8 *f, uint i, uint *symVal, uint *sectNum) {
	char *symName, *strtab;

	image_file_header_t *file = (image_file_header_t *) f;
	image_symbol_t *sym;

	int err;

	// number of symbol table entries
	if (i >= file->NumberOfSymbols) {
		PE_ERROR("index into symbol table (%u) is too large (%u max)\n", i,
			file->NumberOfSymbols);
		return -1;
	}

	// point to symtab entry, get name
	sym = (image_symbol_t *)(f + file->PointerToSymbolTable) + i;
	symName = (char *) sym->ShortName;
	if (sym->Short == 0) {
		strtab = (char *)f + file->PointerToSymbolTable +
			file->NumberOfSymbols * sizeof(image_symbol_t);
		symName = strtab + sym->Long;
	}

	// get section and check it
	if (sym->SectionNumber > file->NumberOfSections) {
		PE_ERROR("symbol '%s' has bad section %d (max %u)\n", symName,
			sym->SectionNumber, file->NumberOfSections);
		return -1;
	}
	
	PE_TRACE("    Symbol type %u sclass %u sect %d value %08x name %s\n",
		sym->Type, sym->StorageClass, sym->SectionNumber, sym->Value, symName);

#if 0	
	// see if the symbol is supposed to come from the kernel, even though it
	// may be in the local symbol table (comdat)
	if (sym->SectionNumber > 0) {
		image_section_header_t *sect =
			(image_section_header_t *)(f + sizeof(image_file_header_t) +
			file->SizeOfOptionalHeader) + (sym->SectionNumber - 1);
		if (sect->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
			*sectNum = 0;
		} else {
			*sectNum = sym->SectionNumber;
		}
	} else {
		*sectNum = sym->SectionNumber;
	}
#else
	*sectNum = sym->SectionNumber;
#endif

	// external symbol
	if (*sectNum == 0) {
		err = lookupExternalSymbol(symName, symVal, true);
		if (err != 0) {
			return err;
		}
	}

	// internal symbol
	else {
		*symVal = sym->Value;
	}

	return 0;
}

static int peCoffReloc(const uint8 *f, uint8 *image,
	image_section_header_t *sect, uint r) {
	uint symSect, symVal, t_adr = 0;
	image_relocation_t *reloc;
	image_file_header_t *file;
	uint32 *where;
	int r_delta, err;

	r_delta = (int)image + sect->PhysicalAddress;

	// point to relocation
	reloc = (image_relocation_t *)(f + sect->PointerToRelocations) + r;
	where = (uint32 *)(reloc->VirtualAddress - sect->VirtualAddress + r_delta);


	PE_TRACE("Relocation %u %s %08x sym index %u type %u\n", r,
		getSectName(f, sect), reloc->VirtualAddress - sect->VirtualAddress,
		reloc->SymbolTableIndex, reloc->Type);

	// get symbol
	err = getCoffSymbol(f, reloc->SymbolTableIndex, &symVal, &symSect);
	if (err != 0) {
		return err;
	}
	
	char name[255];
	getSymName(f, reloc->SymbolTableIndex, name);

	PE_TRACE("Sym sect %d sym val %08x where %08x (%08x) \"%s\"\n", symSect,
		symVal, (uint32) where, *where, name);

	// t_adr = address of target section (section in symbol table entry)
	file = (image_file_header_t *) f;
	if (symSect != 0) {
		// internal symbol
		sect = (image_section_header_t *)(f + sizeof(image_file_header_t) +
			file->SizeOfOptionalHeader) + (symSect - 1);
		t_adr = (uint)image + sect->PhysicalAddress;
		PE_DEBUG("    in sect %s t_adr %08x\n", getSectName(f, sect), t_adr);
	}

	switch (reloc->Type) {
		/* absolute reference
			COFF.H calls this "RELOC_ADDR32"; objdump calls it "dir32" */
		case 6:
			if (symSect == 0) {
				// external symbol
				*where = symVal;
			} else {
				*where += (symVal + t_adr);
			}
			break;

		/* EIP-relative reference
			COFF.H calls this "RELOC_REL32"; objdump calls it "DISP32" */
		case 20:
			if (symSect == 0) {
				// external symbol
				// xxx - 4 = width of relocated item, in bytes. Why should this
				// code have any knowledge of that? Maybe I'm doing something
				// wrong...
				*where = symVal - ((uint)where + 4);
			} else {
				*where += (symVal + t_adr - ((uint)where + 4));
			}
			break;

		default:
			PE_ERROR("unknown relocation type %u (must be 6 or 20)\n",
				reloc->Type);
			return -1;
	}

	return 0;
}


int loadPeExe(const uint8 *f, AddressSpace &aspace, uint32 *entry) {
	uint8 *base;
	image_section_header_t *sect;
	image_optional_header_t *opt;
	image_file_header_t *file;
	image_dos_header_t  *dos;
	uint s, r;
	int err;

	// read the dos header and get the offset of the PECOFF header
	dos = (image_dos_header_t *) f;
	if (dos->e_magic != IMAGE_DOS_SIGNATURE
		|| *(uint32 *) (f + dos->e_lfanew) != IMAGE_NT_SIGNATURE) {
		PE_ERROR("File is not a PECOFF executable; DOS magic %x (expecting %x);"
			" Signature %x (expecting %x)\n", dos->e_magic, IMAGE_DOS_SIGNATURE,
			*(uint32 *) (f + dos->e_lfanew), IMAGE_NT_SIGNATURE);
		return +1;
	} else {
		base = (uint8 *) (f + dos->e_lfanew + 4);
	}

	// validate
	file = (image_file_header_t *) base;
	if (file->Machine != IMAGE_FILE_MACHINE_I386) {
		PE_ERROR("File is not a PECOFF EXE; has bad magic value "
			"0x%x (should be 0x%x)\n", file->Machine, IMAGE_FILE_MACHINE_I386);
		return +1;
	}
	
	// check the optional header
	if (file->SizeOfOptionalHeader) {
		opt = (image_optional_header_t *) (base + sizeof(image_file_header_t));

		PE_DEBUG("Entry point %08x\n", opt->AddressOfEntryPoint);
		PE_DEBUG("Base of code %08x; base of data %08x\n", opt->BaseOfCode,
			opt->BaseOfData);
		PE_DEBUG("Image base %08x\n", opt->ImageBase);
	} else {
		PE_ERROR("Required optional header not found\n");
		return +1;
	}
	
	Log::printf(LOG_LOW, "Code at %08x size %08x\n", opt->ImageBase + opt->BaseOfCode,
		opt->SizeOfCode);
	Log::printf(LOG_LOW, "Data at %08x size %08x\n", opt->ImageBase + opt->BaseOfData,
		opt->SizeOfInitializedData);
	Log::printf(LOG_LOW, "BSS at %08x size %08x\n", opt->ImageBase + opt->BaseOfData
		+ opt->SizeOfInitializedData, opt->SizeOfUninitializedData);
	Log::printf(LOG_LOW, "Heap at %08x size %08x\n", opt->ImageBase + opt->BaseOfData
		+ opt->SizeOfInitializedData + opt->SizeOfUninitializedData,
		opt->SizeOfHeapReserve);
	
	//return +1;
	
	Region *codeRegion = 0;
	Region *dataRegion = 0;
	Region *bssRegion = 0;
	Region *heapRegion = 0;
	
	codeRegion = aspace.create(opt->ImageBase + opt->BaseOfCode, 0,
		PAGE_ROUND(opt->SizeOfCode), REGION_FLAGS_CODE, REGION_TYPE_CODE);
	dataRegion = aspace.create(opt->ImageBase + opt->BaseOfData, 0,
		PAGE_ROUND(opt->SizeOfInitializedData), REGION_FLAGS_DATA,
		REGION_TYPE_DATA);
	bssRegion = aspace.create(opt->ImageBase + opt->BaseOfData
		+ PAGE_ROUND(opt->SizeOfInitializedData), 0,
		PAGE_ROUND(opt->SizeOfUninitializedData), REGION_FLAGS_BSS,
		REGION_TYPE_BSS);
	heapRegion = aspace.create(opt->ImageBase + opt->BaseOfData
		+ PAGE_ROUND(opt->SizeOfInitializedData)
		+ PAGE_ROUND(opt->SizeOfUninitializedData), 0,
		6 * opt->SizeOfHeapReserve, REGION_FLAGS_HEAP, REGION_TYPE_HEAP);
	
	//uint codeOffset = 0;
	//uint dataOffset = 0;
	//uint bssOffset = 0;

	// find the BSS and allocate memory for it
	// This must be done BEFORE doing any relocations
	for (s = 0; s < file->NumberOfSections; s++) {
		sect = (image_section_header_t *)(base + sizeof(image_file_header_t) +
			file->SizeOfOptionalHeader) + s;

		if (sect->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
			continue;
		}
		
		if (sect->Characteristics & IMAGE_SCN_CNT_CODE) {
			r = sect->VirtualAddress - opt->BaseOfCode;
			Log::printf(LOG_LOW, "CODE sect '%.8s': offset %08x, va %08x, size "
				"%08x\n", sect->Name, r, opt->ImageBase + sect->VirtualAddress,
				sect->VirtualSize);
			
			memcpy((uint8 *) codeRegion->getPhysicalBase() + r,
				f + sect->PointerToRawData, sect->SizeOfRawData);
			continue;
		}
		
		if (sect->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
			r = sect->VirtualAddress - opt->BaseOfData;
			Log::printf(LOG_LOW, "DATA sect '%.8s': offset %08x, va %08x, size "
				"%08x\n", sect->Name, r, opt->ImageBase + sect->VirtualAddress,
				sect->VirtualSize);
			
			memcpy((uint8 *) dataRegion->getPhysicalBase() + r,
				f + sect->PointerToRawData, sect->SizeOfRawData);
			continue;
		}
		
		if (sect->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
			r = sect->VirtualAddress - opt->BaseOfData;
			Log::printf(LOG_LOW, "BSS  sect '%.8s': offset %08x, va %08x, size "
				"%08x\n", sect->Name, r, opt->ImageBase + sect->VirtualAddress,
				sect->VirtualSize);
			continue;
		}
	}
	
	//return 1;

	if (file->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
		PE_DEBUG("Relocations have been stripped from COFF file\n");
		//return +1;
	} else {
		// for each section...
		/*for (s = 0; s < file->NumberOfSections; s++) {
			sect = (image_section_header_t *)(base
				+ sizeof(image_file_header_t) + file->SizeOfOptionalHeader) + s;

			// for each relocation...
			for (r = 0; r < sect->NumberOfRelocations; r++) {
				err = peCoffReloc(f, image, sect, r);
				if (err != 0) {
					return err;
				}
			}
			sect++;
		}*/
	}

	*entry = opt->AddressOfEntryPoint + opt->ImageBase;

	return 0;
}	

static int loadPeCoffRelocatable(const uint8 *f, uint8 **image, uint32 *imSize) {
	image_section_header_t *sect;
	image_file_header_t *file;
	uint s, r, value;
	int err;

	// validate
	file = (image_file_header_t *) f;
	if (file->Machine != IMAGE_FILE_MACHINE_I386) {
		PE_ERROR("File is not relocatable COFF; has bad magic value "
			"0x%x (should be 0x%x)\n", file->Machine, IMAGE_FILE_MACHINE_I386);
		return +1;
	}

	// scan the section sizes to determin how much memory to map for module
	uint imageSize = 0;
	for (s=0; s < file->NumberOfSections; s++) {
		sect = (image_section_header_t *)(f + sizeof(image_file_header_t) +
			file->SizeOfOptionalHeader) + s;

#if 0		
		// if the comdat section is defined in the kernel, convert to a
		// discardable section and use the external kernel section instead
		if (sect->Characteristics & IMAGE_SCN_LNK_COMDAT
			&& lookupExternalSymbol(getSectName(f, sect), &value, false) == 0) {
			sect->Characteristics &= ~IMAGE_SCN_LNK_COMDAT;
			sect->Characteristics |= IMAGE_SCN_MEM_DISCARDABLE;
			continue;
		}
		
		if (sect->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
			continue;
		}
#endif
		
		// make use of the unused PA field to keep track of the section address
		sect->PhysicalAddress = imageSize;
		
		PE_TRACE("Section at %08x %s %u bytes %u relocs PA %08x offset %08x "
			"chars %08x\n", sect->PointerToRawData,
			getSectName(f, sect), sect->SizeOfRawData,
			sect->NumberOfRelocations, sect->PhysicalAddress,
			sect->VirtualAddress, sect->Characteristics);

		imageSize += PAGE_ROUND(sect->SizeOfRawData);
	}

	*image = (uint8 *) memalign(PAGE_SIZE, imageSize);
	*imSize = imageSize;

	// copy the sections into the image
	for (s = 0; s < file->NumberOfSections; s++) {
		sect = (image_section_header_t *)(f + sizeof(image_file_header_t) +
			file->SizeOfOptionalHeader) + s;
		
		if (sect->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
			continue;
		}

		if (sect->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
			r = sect->PhysicalAddress;
			PE_DEBUG("Initializing sect %s to 0 at %08x\n",
				getSectName(f, sect), r);

			memset(*image + r, 0, sect->SizeOfRawData);
		} else {
			r = sect->PhysicalAddress;
			PE_DEBUG("Writing sect %s to %08x\n", getSectName(f, sect), r);

			memcpy(*image + r, f + sect->PointerToRawData, sect->SizeOfRawData);
		}
	}

	if (file->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
		PE_ERROR("Relocations have been stripped from COFF file\n");
		return +1;
	} else {
		// for each section...
		for (s = 0; s < file->NumberOfSections; s++) {
			sect = (image_section_header_t *)(f + sizeof(image_file_header_t)
				+ file->SizeOfOptionalHeader) + s;

			// for each relocation...
			for (r = 0; r < sect->NumberOfRelocations; r++) {
				err = peCoffReloc(f, *image, sect, r);
				if (err != 0) {
					PE_ERROR("Error relocating reloc %u in sect %s\n", r,
						getSectName(f, sect));
					return err;
				}
			}
			sect++;
		}
	}

	return 0;
}


// module info

class PeCoffModule : public Module {
	typedef void (*fnptr)(void);
	
	protected:
	
	uint8 *image;
	uint32 size;
	
	// resolved pointers to module_init and module_finalize. may be null
	fnptr init;
	fnptr finalize;
	
	// resolved pointer to dtor list
	int *dtors;
	
	// module name
	char *name;
	
	public:
	
	PeCoffModule(const uint8 *f, uint8 *image, uint32 size);
	virtual ~PeCoffModule(void);
	
	virtual const char *getName(void);
	
	virtual void *resolveSymbol(const char *name);
	
	virtual void callInit(void);
	virtual void callFinalize(void);
};

PeCoffModule::PeCoffModule(const uint8 *f, uint8 *image, uint32 size) {
	this->image = image;
	this->size = size;
	
	// call any static constructors
	int *ctors = (int *) resolveSym(f, image, "__CTOR_LIST__");
	if (ctors[0] == -1) {
		int cindex = 1;
		
		while (ctors[cindex]) {
			fnptr func = (fnptr) ctors[cindex];
			func();
			cindex++;
		}
	}
	
	// find module_init and module_finalize
	init = (fnptr) resolveSym(f, image, "_module_init");
	finalize = (fnptr) resolveSym(f, image, "_module_finalize");
	
	// find the destructor list and save for later
	dtors = (int *) resolveSym(f, image, "__DTOR_LIST__");
	
	char **_name = (char **) resolveSym(f, image, "_module_name");
	if (_name) {
		name = *_name;
	} else {
		name = "unnamed";
	}
}

PeCoffModule::~PeCoffModule(void) {
	// call any static destructors
	if (dtors && dtors[0] == -1) {
		int dindex = 1;
		
		while (dtors[dindex]) {
			fnptr func = (fnptr) dtors[dindex];
			func();
			dindex++;
		}
	}
	
	delete image;
}

const char *PeCoffModule::getName(void) {
	return name;
}

void *PeCoffModule::resolveSymbol(const char *name) {
	return 0;
}

void PeCoffModule::callInit(void) {
	if (init) {
		init();
	}
}

void PeCoffModule::callFinalize(void) {
	if (finalize) {
		finalize();
	}
}

int loadPeCoffRelocatable(const uint8 *f, Module **module) {
	uint8 *image;
	uint32 size;
	int ret;
		
	if (f == 0 || module == 0) {
		ret = -1;
		goto error;
	}
	
	ret = loadPeCoffRelocatable(f, &image, &size);
	
	if (ret == 0) {
		printf("Loaded PE-COFF module at %08x\n", (uint32) image);
		*module = new PeCoffModule(f, image, size);
	}	
	
	error:
	return ret;
}

#if 0

PeCoffFile::PeCoffFile(const uint8 *d, int len) {
	data = new uint8[len];
	memcpy(data, d, len);
	
	uint8 *base = 0;

	// read the dos header and get the offset of the PECOFF header
	dos = (image_dos_header_t *) data;
	if (dos->e_magic != IMAGE_DOS_SIGNATURE
		|| *(uint32 *) (data + dos->e_lfanew) != IMAGE_NT_SIGNATURE) {
		PE_ERROR("File is not a PECOFF executable; DOS magic %x (expecting %x);"
			" Signature %x (expecting %x)\n", dos->e_magic, IMAGE_DOS_SIGNATURE,
			*(uint32 *) (data + dos->e_lfanew), IMAGE_NT_SIGNATURE);
		
		dos = 0;
	} else {
		base = (uint8 *) (data + dos->e_lfanew + 4);
	}
	
	// validate
	file = (image_file_header_t *) base;
	if (file->Machine != IMAGE_FILE_MACHINE_I386) {
		PE_ERROR("File is not a PECOFF EXE; has bad magic value "
			"%x (should be %x)\n", file->Machine, IMAGE_FILE_MACHINE_I386);
		valid = false;
		return;
	}

	// check the optional header
	if (file->SizeOfOptionalHeader) {
		opt = (image_optional_header_t *) (base + sizeof(image_file_header_t));

		PE_DEBUG("Entry point %08x\n", opt->AddressOfEntryPoint);
		PE_DEBUG("Base of code %08x; base of data %08x\n", opt->BaseOfCode,
			opt->BaseOfData);
		PE_DEBUG("Image base %08x\n", opt->ImageBase);
	} else {
		PE_ERROR("Optional header not found\n");
		opt = 0;
	}

	valid = true;
}

PeCoffFile::~PeCoffFile(void) {
	delete data;
}

bool PeCoffFile::reloc(uint8 *image, image_section_header_t *sect, uint r) {
	uint symSect, symVal, t_adr = 0;
	image_relocation_t *reloc;
	uint32 *where;
	int r_delta;

	r_delta = (int)image + sect->VirtualAddress;

	// point to relocation
	reloc = (image_relocation_t *)(data + sect->PointerToRelocations) + r;
	where = (uint32 *)(reloc->VirtualAddress + r_delta);


	PE_DEBUG("Relocation %u %s %08x sym index %u type %u\n", r,
		getSectName(data, sect), reloc->VirtualAddress, reloc->SymbolTableIndex,
		reloc->Type);

	// get symbol
	if (!symbol(reloc->SymbolTableIndex, &symVal, &symSect)) {
		return false;
	}

	PE_DEBUG("Sym sect %d sym val %08x where %08x (%08x)\n", symSect,
		symVal, (uint32) where, *where);

	// t_adr = address of target section (section in symbol table entry)
	if (symSect != 0) {
		// internal symbol
		sect = (image_section_header_t *)(data + sizeof(image_file_header_t) +
			file->SizeOfOptionalHeader) + (symSect - 1);
		t_adr = (uint)image + sect->VirtualAddress;
		PE_DEBUG("    in sect %s t_adr %08x\n", getSectName(data, sect), t_adr);
	}

	switch (reloc->Type) {
		/* absolute reference
			COFF.H calls this "RELOC_ADDR32"; objdump calls it "dir32" */
		case 6:
			if (symSect == 0) {
				// external symbol
				*where = symVal;
			} else {
				*where += (symVal + t_adr);
			}
			break;

		/* EIP-relative reference
			COFF.H calls this "RELOC_REL32"; objdump calls it "DISP32" */
		case 20:
			if (symSect == 0) {
				// external symbol
				// xxx - 4 = width of relocated item, in bytes. Why should this
				// code have any knowledge of that? Maybe I'm doing something
				// wrong...
				*where = symVal - ((uint)where + 4);
			} else {
				*where += (symVal + t_adr - ((uint)where + 4));
			}
			break;

		default:
			PE_ERROR("unknown relocation type %u (must be 6 or 20)\n",
				reloc->Type);
			return false;
	}
	
	return true;
}

bool PeCoffFile::symbol(uint32 i, uint32 *symVal, uint32 *sectNum) {
	char *symName, *strtab;

	image_symbol_t *sym;

	// number of symbol table entries
	if (i >= file->NumberOfSymbols) {
		PE_ERROR("index into symbol table (%u) is too large (%u max)\n", i,
			file->NumberOfSymbols);
		return false;
	}

	// point to symtab entry, get name
	sym = (image_symbol_t *)(data + file->PointerToSymbolTable) + i;
	symName = (char *) sym->ShortName;
	if (sym->Short == 0) {
		strtab = (char *)data + file->PointerToSymbolTable +
			file->NumberOfSymbols * sizeof(image_symbol_t);
		symName = strtab + sym->Long;
	}

	// get section and check it
	if (sym->SectionNumber > (sint32) file->NumberOfSymbols) {
		PE_ERROR("symbol '%s' has bad section %d (max %u)\n", symName,
			sym->SectionNumber, file->NumberOfSymbols);
		return false;
	}

	*sectNum = sym->SectionNumber;
	// external symbol
	if (*sectNum == 0) {
		if (lookupExternalSymbol(symName, symVal, true) != 0) {
			return false;
		}
	}

	// internal symbol
	else {
		*symVal = sym->Value;
	}

	return true;
}

uint32 PeCoffFile::resolve(uint8 *image, const char *name) {
	image_symbol_t *sym = 0;

	for (uint32 i=0; i < file->NumberOfSymbols; i++) {
		image_symbol_t *tmp =
			(image_symbol_t *)(data + file->PointerToSymbolTable) + i;

		if (tmp->Short) {
			if (!strncmp(name, sym->ShortName, 8)) {
				sym = tmp;
				break;
			}
		} else if (tmp->Long > 0) {
			char *strTab = (char *)data + file->PointerToSymbolTable +
			file->NumberOfSymbols * sizeof(image_symbol_t);

			if (!strcmp(name, strTab + tmp->Long)) {
				sym = tmp;
				break;
			}
		}
	}

	if (sym && sym->SectionNumber > 0) {
		// internal symbol
		image_section_header_t * sect = (image_section_header_t *)(data
			+ sizeof(image_file_header_t) + file->SizeOfOptionalHeader)
			+ (sym->SectionNumber - 1);

		return (uint)image + sect->VirtualAddress + sym->Value;
	} else {
		return 0;
	}
}

bool PeCoffFile::loadExecutable(uint32 **base, uint32 **entry) {
}

bool PeCoffFile::loadModule(void) {
}

bool PeCoffFile::validate(void) {
}

#endif
